package de.iceboy.android.goldrush.view;

import de.iceboy.android.goldrush.data.Hit;
import de.iceboy.android.goldrush.data.HitSet;
import de.iceboy.android.goldrush.enums.TargetColor;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Paint.Style;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;

public class TargetView extends View {
	private static final String TAG = "Goldrush - Target";
	public static final int RINGS = 10;
	public static final int RING_BOUND = 21;
	public static final int LOWER_BOUND = 0;
	public static final int UPPDER_BOUND = 16384;
	public static final int TOTAL_RANGE = UPPDER_BOUND - LOWER_BOUND;
	public static final int CENTER = TOTAL_RANGE / 2;
	public static final int RING_RANGE = (TOTAL_RANGE / 2) / RINGS;
	public static final int XRING_RANGE = RING_RANGE / 2;
	private static final int CENTER_HEIGHT = 52;
	private static final int CENTER_STROKE_WITDH = 21;

	private final Paint paint[] = new Paint[7];

	private int zoomFactor = 10;
	private double realZoomFactor = 1;
	private int offsetX = 0;
	private int offsetY = 0;
	private DashPathEffect dashEffect;
	private double converstionFactor;
	private int canvasWidth;
	private int canvasHeight;
	private int canvasMin;
	private HitSet hitSet;

	public TargetView(Context context) {
		super(context);
		initPaints();
		setupColors();
	}

	private void initPaints() {
		for (int index = 0; index <= TargetColor.Arrow.getIndex(); index++) {
			paint[index] = new Paint(Paint.ANTI_ALIAS_FLAG);
		}
		paint[TargetColor.BlackX.getIndex()].setStyle(Style.STROKE);

		paint[TargetColor.BlackX.getIndex()].setStyle(Style.STROKE);

	}

	private void setupColors() {
		setColor(TargetColor.White, 0xFFFFFFFF);
		setColor(TargetColor.Black, 0xFF000000);
		setColor(TargetColor.Blue, 0xFF0000FF);
		setColor(TargetColor.Red, 0xFFFF0000);
		setColor(TargetColor.Gold, 0xFFFFFF00);
		setColor(TargetColor.BlackX, 0xFF000000);
		setColor(TargetColor.Arrow, 0xFF00FF00);
	}

	private void setColor(TargetColor color, int value) {
		paint[color.getIndex()].setColor(value);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		updateCanvasFactors(canvas);
		updateZoomFactor();
		for (int i = 0; i < RINGS; i++)
			drawRing(canvas, CENTER - i * RING_RANGE, paint[i / 2], paint[i == 3 ? 0 : 1]);
		drawCenter(canvas);
		drawHits(canvas);
	}


	private void updateCanvasFactors(Canvas canvas) {
		canvasWidth = canvas.getClipBounds().right - canvas.getClipBounds().left;
		Log.d(TAG, canvasWidth + " . " + canvas.getWidth() + " -- " + canvas.getClipBounds().right + " _ " + canvas.getClipBounds().left);
		canvasHeight = canvas.getClipBounds().bottom - canvas.getClipBounds().top;
		canvasMin = Math.max(Math.min(canvasHeight, canvasWidth), 1);
		Log.d(TAG, "canvas Min: " + canvasMin);
		converstionFactor = TOTAL_RANGE / ((double)canvasMin);
	}

	private void updateZoomFactor() {
		realZoomFactor = (zoomFactor / 10.0) * converstionFactor;
		Log.d(TAG, "realZoomFactor: " + realZoomFactor);
	}

	private void drawRing(Canvas canvas, int radius, Paint p, Paint outerBorder) {
		canvas.drawCircle(zoom(CENTER - offsetX), zoom(CENTER - offsetY), zoom(radius), outerBorder);
		canvas.drawCircle(zoom(CENTER - offsetX), zoom(CENTER - offsetY), zoom(radius - RING_BOUND), p);
	}

	private void drawCenter(Canvas canvas) {
		drawXRing(canvas);
		drawCross(canvas);
	}

	private void drawXRing(Canvas canvas) {
		// TODO Auto-generated method stub
		updateStrokeWidth();
		canvas.drawCircle(zoom(CENTER - offsetX), zoom(CENTER - offsetY), zoom(RING_RANGE / 2), paint[TargetColor.BlackX
				.getIndex()]);
	}

	private void updateStrokeWidth() {
		dashEffect = new DashPathEffect(new float[] { zoom(25), zoom(25) }, 1);
		paint[TargetColor.BlackX.getIndex()].setPathEffect(dashEffect);
		paint[TargetColor.BlackX.getIndex()].setStrokeWidth(zoom(RING_BOUND / 2));
	}

	private void drawCross(Canvas canvas) {
		Rect vertical = new Rect(zoom(CENTER - offsetX - CENTER_STROKE_WITDH / 2), zoom(CENTER - offsetY - CENTER_HEIGHT / 2),
				zoom(CENTER - offsetX + CENTER_STROKE_WITDH / 2), zoom(CENTER - offsetY + CENTER_HEIGHT / 2));
		Rect horizontal = new Rect(zoom(CENTER - offsetX - CENTER_HEIGHT / 2), zoom(CENTER - offsetY - CENTER_STROKE_WITDH / 2),
				zoom(CENTER - offsetX + CENTER_HEIGHT / 2), zoom(CENTER - offsetY + CENTER_STROKE_WITDH / 2));
		canvas.drawRect(vertical, paint[TargetColor.Black.getIndex()]);
		canvas.drawRect(horizontal, paint[TargetColor.Black.getIndex()]);
	}
	private void drawHits(Canvas canvas) {
		Hit h;
		for(int i = 0; i < hitSet.getCount(); i++){
			h = hitSet.getHit(i);
			canvas.drawCircle(zoom(h.getX()-offsetX), zoom(h.getY()-offsetY), 6, paint[TargetColor.Arrow.getIndex()]);
		}
	}

	private int zoom(int ringRange) {
		return (int) (ringRange / realZoomFactor);
	}

	private int unZoom(float value) {
		return (int) (value * realZoomFactor);
	}

	public boolean zoomOut() {
		boolean ret = zoomByFactor(1);

		validateOffsets();
		invalidate();
		return ret;
	}

	public boolean zoomIn() {
		boolean ret = zoomByFactor(-1);
		validateOffsets();
		invalidate();
		return ret;
	}

	private boolean zoomByFactor(int factor) {
		boolean ret = true;
		zoomFactor += factor;
		updateZoomFactor();
		Log.d(TAG, "ZFactor: " + zoomFactor);
		if (validateZoomFactor()) {
			Log.d(TAG, "ZFactor OK");
			offsetX -= CENTER * (factor / 10.0);
			offsetY -= CENTER * (factor / 10.0);
		} else
			ret = false;
		return ret;
	}

	private boolean validateZoomFactor() {
		if (zoomFactor < 1) {
			zoomFactor = 1;
			return false;
		}
		if (zoomFactor > 10) {
			zoomFactor = 10;
			return false;
		}
		return true;
	}
	
	private boolean isValidZoomFactor(int factor){
		if (factor < 1||factor > 10) {
			return false;
		}
		return true;
	}

	public void move(float x, float y) {
		offsetX += unZoom(x);
		offsetY += unZoom(y);
		validateOffsets();
		invalidate();
		//Log.d(TAG, "Moved: " + unZoom(x) + ", " + unZoom(y) + " to pos " + (CENTER + offsetX) + ", " + (CENTER + offsetY));
	}

	private void validateOffsets() {
		//Log.d(TAG, "Offsets: " + offsetX + " " + offsetY);
		offsetX = Math.max(LOWER_BOUND, Math.min(offsetX, UPPDER_BOUND - unZoom(canvasMin)));
		offsetY = Math.max(LOWER_BOUND, Math.min(offsetY, UPPDER_BOUND - unZoom(canvasMin)));
		//Log.d(TAG, "Offsets: " + offsetX + " " + offsetY);
	}

	public boolean inCircle(float startX, float startY) {
		int distance = (int) Math.sqrt(Math.pow((unZoom(startX) - (CENTER - offsetX)), 2)
				+ Math.pow((unZoom(startY) - (CENTER - offsetY)), 2));
		/*Log.d("Goldrush", startX + " " + startY + " - " + unZoom(startX) + " " + unZoom(startY) + "("
				+ (unZoom(startX) - (CENTER - offsetX)) + "," + (unZoom(startY) - (CENTER - offsetY)) + ")" + " distance: "
				+ distance);
				*/
		return distance < CENTER;
	}

	public void zoomIn(float x, float y) {
		Log.d(TAG, offsetX + ", " + offsetY + " - " + (unZoom(x) - unZoom(canvasMin) / 2) + ", " + (unZoom(y) - unZoom(canvasMin) / 2) + " - " + unZoom(canvasMin) + ", "
				+ unZoom(canvasMin) / 2 + "  --  " + x + ", " + y);
		if(!isValidZoomFactor(zoomFactor-2))
			return;
		offsetX += unZoom(x) - unZoom(canvasMin) / 2;
		offsetY += unZoom(y) - unZoom(canvasMin) / 2;
		zoomByFactor(-2);
		offsetX -= unZoom(x) - unZoom(canvasMin) / 2;
		offsetY -= unZoom(y) - unZoom(canvasMin) / 2;
		validateZoomFactor();
		validateOffsets();
		invalidate();
		Log.d(TAG, offsetX + ", " + offsetY + " - " + (unZoom(x) - unZoom(canvasMin) / 2) + ", " + (unZoom(y) - unZoom(canvasMin) / 2) + " - " + unZoom(canvasMin) + ", "
				+ unZoom(canvasMin) / 2 + "  --  " + x + ", " + y);
	}

	public Hit getHit(float x, float y) {
		return new Hit(unZoom(x) + offsetX, unZoom(y) + offsetY);
	}
	
	public void setHitSet(HitSet hs){
		hitSet = hs;
		this.invalidate();
	}

	public void zoomCompleteOut() {
		zoomFactor = 10;
		offsetX = 0;
		offsetY = 0;
		updateZoomFactor();
		invalidate();
	}
	
}
